local lib = LibStub:NewLibrary("qqlib-1.0", 1)

if not lib then
  return	-- already loaded and no upgrade necessary
end

--[[
local verbose = print and type(print)=='function'
local f = Function('x,y')
local code = f.code
code('local a=x*15')
code('print("a is "..a)',verbose)
code('return a+y+UnitHealth("player")+z')

f = f.using{UnitHealth=UnitHealth, z=3, print=print}

print(assert(f(1,2)))
]]
--[[
FunctionObj = Function(args)
args is a string

FunctionObj API
f = FunctionObj:create()
f is a function 

--]]
local function Function(args)
	local o = {}
	local data = {}
	local env = {}

	args = args or ""

	local function code(line,...)
		for i=1,select('#',...) do
			if not select(i,...) then 
				return
			end
		end
		table.insert(data,line)
	end


	local function __index(t,k)
		ChatFrame1:AddMessage('undefined upvalue '..tostring(k), 1, 0.3, 0.3)
		local v = _G[k]
		if v then
			env[k] = v
			return v
		end
	end
	setmetatable(env, {__index=__index})

	-- TODO args
	local function inline(argnames)
		local joined = strjoin('\n',unpack(data))
		data = {joined}
		return string.format('do %s end', joined)
	end

	local function body()
		local joined = strjoin('\n',unpack(data))
		data = {joined}
		return joined
	end

	local function asstring()
		local joined = body()
		return string.format('return function(%s) %s end',args,joined)
	end

	local function create()
		local result = assert(loadstring(asstring()))
		if not result then
			return
		end
		local func = result()
		setfenv(func, env)
		return func
	end

	local function using(fenv)
		local code = asstring()

		local defs = {'return function(fenv)'}
		for k,_ in pairs(fenv) do
			if type(k) == "string" then
				table.insert(defs, string.format(' local %s = fenv.%s',k,k))
			else
				ChatFrame1:AddMessage('error: not a string '..tostring(k), 1, 0.3, 0.3)
			end
		end
		table.insert(defs, code)
		table.insert(defs, 'end')

		local joined = strjoin('\n',unpack(defs))

		local result = assert(loadstring(joined))
		if not result then
			return
		end
		local func = result()
		func = func(fenv)
		setfenv(func, env)
		return func
	end

	o.code = code
	o.body = body
	o.asstring = asstring
	o.inline = inline
	o.create = create
	o.using = using

	return o
end

lib.Function = Function
